package org.phantom.securityframework.local;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.ibatis.session.SqlSession;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.subject.Subject;
import org.phantom.securityframework.api.Account;
import org.phantom.securityframework.api.AccountExt;
import org.phantom.securityframework.api.AccountManager;
import org.phantom.securityframework.api.GroupManager;
import org.phantom.securityframework.api.Permission;
import org.phantom.securityframework.api.PermissionManager;
import org.phantom.securityframework.api.RoleManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import org.phantom.securityframework.api.crypt.CryptUtil;
import org.phantom.securityframework.api.crypt.Encrypt;
import org.phantom.securityframework.api.datapermission.DataDimension;
import org.phantom.securityframework.api.datapermission.DimensionType;
import org.phantom.securityframework.api.domain.AccountQuery;
import org.phantom.securityframework.api.domain.Group;
import org.phantom.securityframework.api.domain.Role;
import org.phantom.securityframework.auth.realm.SubjectDataPermissionRealm;
import org.phantom.securityframework.auth.realm.SubjectRealm;
/**
 * 帐号接口本地实现
 * @author david
 *
 */
@Service("accountManagerLocalImpl")
@Transactional
public class AccountManagerLocalImpl implements AccountManager,SubjectRealm,SubjectDataPermissionRealm {

	@Resource
	private SqlSession session;

	@Resource
	protected GroupManager groupManager=null;
	@Resource
	protected RoleManager roleManager = null;
	@Resource
	protected PermissionManager permissionManager = null;
	
	private Account register(Account a,Serializable[] roleIds,Serializable[] groupIds,Serializable[] permissionIds,AuthStatus status){
		org.phantom.securityframework.api.domain.Account account = (org.phantom.securityframework.api.domain.Account) a;
		if(account==null) return null;
		if(account.getAccountId()==null||"".equals(account.getAccountId())){
			throw new RuntimeException("accountId can not be null!");
		}
		Account temp = this.get(account.getAccountId());
		if(temp!=null){
			throw new AccountException("accountId must be unique!");
		}
		account.setStatus(AuthStatus.getIndex(status));
		session.insert("org.phantom.securityframework.AccountMapper.saveAccount",account);
		
		
		if(roleIds!=null && roleIds.length>0){
			roleManager.relAccountRole(account, roleIds);
		}
		if(account.getRoles()!=null && account.getRoles().size()>0){
			roleManager.relAccountRole(account, account.getRoles().toArray(new Role[]{}));
		}
		
		if(groupIds!=null && groupIds.length>0){
			groupManager.relAccountGroup(account, groupIds);
		}
		if(account.getGroups()!=null && account.getGroups().size()>0){
			groupManager.relAccountGroup(account, account.getGroups().toArray(new Group[]{}));
		}

		if(permissionIds!=null && permissionIds.length>0){
			permissionManager.relAccountPermission(account, permissionIds);
		}
		if(account.getPermissions()!=null && account.getPermissions().size()>0){
			permissionManager.relAccountPermission(account, account.getPermissions().toArray(new Permission[]{}));
		}
		addAccountExt(account);
		return account;
	}

	protected Account addAccountExt(Account a){
		org.phantom.securityframework.api.domain.Account account = (org.phantom.securityframework.api.domain.Account) a;
		if(account.getAccountExts()!=null && account.getAccountExts().size()>0){
			for (org.phantom.securityframework.api.AccountExt ext : account.getAccountExts()) {
				ext.setAccountId(account.getId());
				session.insert("org.phantom.securityframework.AccountExtMapper.saveAccountExt", ext);
			}
		}
		
		return account;
	}
	
	public Account register(Account account) {
		return this.register(account, null,null,null,AuthStatus.ACTIVATION);
	}

	public Account register(Account account, AuthStatus AuthStatus) {
		return this.register(account,null,null,null,AuthStatus);
	}
	public Account register(Account a, AuthStatus AuthStatus,Encrypt encrypt) throws Exception {
		org.phantom.securityframework.api.domain.Account account = (org.phantom.securityframework.api.domain.Account) a;
		if(account!=null && encrypt!=null)
			account.setPassword(CryptUtil.encodeBytes(encrypt.encrypt(account.getPassword().getBytes(), null)));
		return this.register(account, null,null,null,AuthStatus);
	}
	
	public Account registerWithRoles(Account account, Serializable[] roleIds) {
		return this.register(account, roleIds,null,null,AuthStatus.ACTIVATION);
	}

	public Account registerWithRoles(Account account, Serializable[] roleIds,AuthStatus AuthStatus) {
		return this.register(account, roleIds,null,null,AuthStatus);
	}

	public Account registerWithRoles(Account account,Role[] roles){
		return this.registerWithRoles(account, roles, AuthStatus.ACTIVATION);
	}
	public Account registerWithRoles(Account account,Role[] roles,AuthStatus accountStatus){
		List<Serializable> list = new ArrayList<Serializable>();
		for (Role role : roles) {
			list.add(role.getId());
		}
		return this.register(account, list.toArray(new Serializable[]{}),null,null,accountStatus);
	}

	public Account registerWithGroups(Account account, Serializable[] groupIds) {
		return this.register(account, null,groupIds,null,AuthStatus.ACTIVATION);
	}
	public Account registerWithGroups(Account account, Serializable[] groupIds, AuthStatus AuthStatus) {
		return this.register(account, null,groupIds,null,AuthStatus);
	}
	public Account registerWithGroups(Account account,Group[] groups){
		return this.registerWithGroups(account, groups,AuthStatus.ACTIVATION);
	}
	public Account registerWithGroups(Account account,Group[] groups,AuthStatus accountStatus){
		List<Serializable> list = new ArrayList<Serializable>();
		for (Group g : groups) {
			list.add(g.getId());
		}
		return this.register(account, null,list.toArray(new Serializable[]{}),null,accountStatus);
		
	}

	public Account registerWithPermissions(Account account, Serializable[] permissionIds) {
		return this.register(account, null,null,permissionIds,AuthStatus.ACTIVATION);
	}

	public Account registerWithPermissions(Account account, Serializable[] permissionIds, AuthStatus AuthStatus) {
		return this.register(account, null,null,permissionIds,AuthStatus);
	}

	public Account registerWithPermissions(Account account,Permission[] permissions){
		return this.registerWithPermissions(account, permissions,AuthStatus.ACTIVATION);
	}
	public Account registerWithPermissions(Account account,Permission[] permissions,AuthStatus accountStatus){
		List<Serializable> list = new ArrayList<Serializable>();
		for (Permission p : permissions) {
			list.add(p.getId());
		}
		return this.register(account, null,null,list.toArray(new Serializable[]{}),accountStatus);
	}

	public Account modify(Account a) {
		org.phantom.securityframework.api.domain.Account account = (org.phantom.securityframework.api.domain.Account) a;
		if(account.getId()==null || account.getId().intValue()==0){
			throw new RuntimeException("id can not be null");
		}
		this.session.update("org.phantom.securityframework.AccountMapper.modifyAccount",account);
		modifyAccountExt(account);
		return account;
	}

	protected Account modifyAccountExt(Account a){
		org.phantom.securityframework.api.domain.Account account = (org.phantom.securityframework.api.domain.Account) a;
		if(account.getAccountExts()!=null){
			
			for(org.phantom.securityframework.api.AccountExt ext : account.getAccountExts()){
				this.session.update("org.phantom.securityframework.AccountExtMapper.modifyAccountExt",ext);
			}
		}
		return account;
	}
	
	public Account remove(Account a) {
		org.phantom.securityframework.api.domain.Account account = (org.phantom.securityframework.api.domain.Account) a;
		if(account.getId()==null || account.getId().intValue()==0){
			throw new RuntimeException("id can not ne null!");
		}
		this.session.delete("org.phantom.securityframework.AccountMapper.removeAccount", account);
		
		removeExt(account.getAccountExts());
		return null;
	}
	public Account remove(Serializable id){
		if(id==null){
			throw new RuntimeException("id can not ne null!");
		}
		return this.remove(this.get(id));
	}
	
	protected void removeExt(List<AccountExt> exts){
		List<Integer> ids = new ArrayList<Integer>();
		for(org.phantom.securityframework.api.AccountExt ext : exts){
			ids.add(ext.getId());
		}
		this.session.delete("org.phantom.securityframework.AccountExtMapper.removeAccountExt", ids);
	}

	public Account activate(Account a) {
		org.phantom.securityframework.api.domain.Account account = (org.phantom.securityframework.api.domain.Account) a;
		if(account.getId()==null || account.getId().intValue()==0){
			throw new RuntimeException("id can not be null");
		}
		account.setStatus(AuthStatus.ACTIVATION.getIndex());
		this.session.update("org.phantom.securityframework.AccountMapper.modifyAccount",account);
		return account;
	}

	public Account unactivate(Account a) {
		org.phantom.securityframework.api.domain.Account account = (org.phantom.securityframework.api.domain.Account) a;
		if(account.getId()==null || account.getId().intValue()==0){
			throw new RuntimeException("accountid can not be null");
		}
		account.setStatus(AuthStatus.UNACTIVATION.getIndex());
		this.session.update("org.phantom.securityframework.AccountMapper.modifyAccount",account);
		return account;
	}

	public Account get(Serializable id) {
		return (Account) this.session.selectOne("org.phantom.securityframework.AccountMapper.getAccountById", id);
	}

	public Account get(String accountId) {
		AccountQuery query = new AccountQuery();
		query.setAccountId(accountId);
		return (Account) this.session.selectOne("org.phantom.securityframework.AccountMapper.getAccountByAccountId", query);
	}

	public Account get(AccountQuery query) {
		return null;
	}

	@SuppressWarnings("unchecked")
	public List<Account> list() {
		AccountQuery q = new AccountQuery();
		
		return session.selectList("org.phantom.securityframework.AccountMapper.list",q);
	}

	@SuppressWarnings("unchecked")
	public List<Account> list(AccountQuery query) {
		if(query==null)
			return this.list();
		return session.selectList("org.phantom.securityframework.AccountMapper.list",query);
	}
	public Integer count(AccountQuery query){
		if(query == null)return this.count();
		return (Integer) session.selectOne("org.phantom.securityframework.AccountMapper.count", query);
	}

	@Override
	public Integer count() {
		AccountQuery q = new AccountQuery();
		return (Integer) session.selectOne("org.phantom.securityframework.AccountMapper.count",q);
	}

	@Override
	public Subject getSubject(String principal) {
		return this.get(principal);
	}
	@Override
	public Subject recordLogin(Subject s){
		org.phantom.securityframework.api.domain.Account a = (org.phantom.securityframework.api.domain.Account) s;
		a.setLastLoginTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
		a.setAccountExts(null);
		return this.modify(a);
	}

	@Override
	public void setDataDimension(Account account, DataDimension dimension) {
		// TODO Auto-generated method stub
		
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<DimensionType> getSubjectDataDimensins(String accountId,String dimensionDesc) {
		Map<String,String> map = new HashMap<String,String>();
		map.put("accountId", accountId);
		map.put("dimension", dimensionDesc);
		List<DimensionType> list = session.selectList("org.phantom.securityframework.AccountMapper.listDimension",map);
		return list;
	}
}
